"""
漫画分镜分割模块
专门用于识别漫画分镜间隔，将漫画精确分割成独立的分镜
"""

import base64
import json
import requests
from typing import Dict, List, Any, Optional, Tuple
from PIL import Image, ImageDraw, ImageFilter, ImageStat, ImageChops
import logging
import asyncio
import time
from pathlib import Path
import subprocess
import math
import numpy as np
from collections import defaultdict

class MangaPanelSplitter:
    """漫画分镜分割器 - 精确识别并分割每个独立分镜"""
    
    def __init__(self, config: Dict[str, Any]):
        """初始化分镜分割器"""
        self.config = config
        self.logger = logging.getLogger('image_cropping_system')
        
        # 分镜检测配置
        self.black_threshold = config.get('manga_splitting', {}).get('black_threshold', 30)
        self.min_panel_size = config.get('manga_splitting', {}).get('min_panel_size', 100)
        self.gap_threshold = config.get('manga_splitting', {}).get('gap_threshold', 10)
        self.merge_small_panels = config.get('manga_splitting', {}).get('merge_small_panels', True)
        
        # AI检测配置
        self.enable_ai_detection = config.get('manga_splitting', {}).get('enable_ai_detection', True)
        self.ai_timeout = config.get('manga_splitting', {}).get('ai_timeout', 30)
        
        # 右侧检测配置
        self.enable_right_edge_detection = config.get('manga_splitting', {}).get('enable_right_edge_detection', True)
        self.right_edge_sensitivity = config.get('manga_splitting', {}).get('right_edge_sensitivity', 1.5)
        self.right_edge_coverage = config.get('manga_splitting', {}).get('right_edge_coverage', 0.33)
        
        # Ollama 配置
        self.ollama_config = config.get('ollama', {})
        self.base_url = self.ollama_config.get('base_url', 'http://localhost:6399')
        self.default_model = self.ollama_config.get('model', 'qwen2.5vl:7b')
        self.timeout = self.ollama_config.get('timeout', 120)
        
        self.current_model = None
    
    def get_current_model(self) -> str:
        """获取当前使用的模型"""
        if not self.current_model:
            self.current_model = self.default_model
        return self.current_model
    
    async def split_manga_panels(self, files: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """分割漫画分镜"""
        results = []
        
        for file_info in files:
            try:
                self.logger.info(f"开始漫画分镜分割: {file_info.get('original_name', file_info.get('processed_name', 'unknown'))}")
                
                # 检测分镜边界
                panels = await self._detect_panels(file_info['processed_path'])
                
                if not panels:
                    self.logger.warning(f"未检测到分镜，使用全图: {file_info.get('original_name', file_info.get('processed_name', 'unknown'))}")
                    panels = [{
                        'bbox': [0, 0, file_info['width'], file_info['height']],
                        'confidence': 0.5,
                        'panel_type': 'full_image'
                    }]
                
                result = {
                    **file_info,
                    'panels': panels,
                    'panel_count': len(panels),
                    'splitting_status': 'success'
                }
                
                results.append(result)
                
                # 保存可视化结果
                if self.config.get('output', {}).get('save_bbox_visualization', True):
                    await self._save_panel_visualization(file_info, panels)
                
            except Exception as e:
                self.logger.error(f"分镜分割失败 {file_info.get('original_name', file_info.get('processed_name', 'unknown'))}: {e}")
                results.append({
                    **file_info,
                    'panels': [{
                        'bbox': [0, 0, file_info['width'], file_info['height']],
                        'confidence': 0.0,
                        'panel_type': 'error'
                    }],
                    'panel_count': 1,
                    'splitting_status': 'error',
                    'error': str(e)
                })
        
        return results
    
    async def _detect_panels(self, image_path: str) -> List[Dict[str, Any]]:
        """检测漫画分镜"""
        try:
            with Image.open(image_path) as img:
                width, height = img.size
                
            # 方法1：基于线条检测的分镜识别
            line_panels = await self._detect_panels_by_lines(image_path)
            
            # 方法2：基于颜色区域的分镜识别
            color_panels = await self._detect_panels_by_color_regions(image_path)
            
            # 方法3：AI辅助分镜识别（可选，失败不影响整体流程）
            ai_panels = []
            if self.enable_ai_detection:
                try:
                    ai_panels = await self._detect_panels_by_ai(image_path)
                except Exception as e:
                    self.logger.warning(f"AI分镜检测跳过: {e}")
                    ai_panels = []
            else:
                self.logger.info("AI检测已禁用，使用基础检测方法")
            
            # 方法4：右侧区域强化检测（针对宽图右侧遗漏问题）
            right_edge_panels = []
            if self.enable_right_edge_detection:
                right_edge_panels = await self._detect_right_edge_panels(image_path, width, height)
            else:
                self.logger.info("右侧区域检测已禁用")
            
            # 合并和优化结果
            final_panels = await self._merge_and_optimize_panels(
                line_panels, color_panels, ai_panels, right_edge_panels, width, height
            )
            
            self.logger.info(f"检测到 {len(final_panels)} 个分镜")
            return final_panels
            
        except Exception as e:
            self.logger.error(f"分镜检测失败: {e}")
            return []
    
    async def _detect_panels_by_lines(self, image_path: str) -> List[Dict[str, Any]]:
        """基于线条检测分镜"""
        try:
            with Image.open(image_path) as img:
                # 转换为灰度图
                gray = img.convert('L')
                
                # 边缘检测
                edges = gray.filter(ImageFilter.FIND_EDGES)
                
                # 二值化
                threshold = 50
                binary = edges.point(lambda x: 0 if x < threshold else 255, '1')
                
                # 查找水平和垂直线条
                h_lines = self._find_horizontal_lines(binary)
                v_lines = self._find_vertical_lines(binary)
                
                # 基于线条构建分镜网格
                panels = self._build_panels_from_lines(h_lines, v_lines, img.size)
                
                return panels
                
        except Exception as e:
            self.logger.error(f"线条检测分镜失败: {e}")
            return []
    
    def _find_horizontal_lines(self, binary_img: Image.Image) -> List[int]:
        """查找水平线条"""
        width, height = binary_img.size
        h_lines = []
        
        for y in range(height):
            # 计算这一行的黑色像素比例
            row_pixels = list(binary_img.crop((0, y, width, y + 1)).getdata())
            black_ratio = sum(1 for pixel in row_pixels if pixel == 0) / len(row_pixels)
            
            # 如果黑色像素比例高，认为是分镜线
            if black_ratio > 0.7:  # 70%以上是黑色
                h_lines.append(y)
        
        # 合并接近的线条
        merged_lines = []
        for line in h_lines:
            if not merged_lines or line - merged_lines[-1] > self.gap_threshold:
                merged_lines.append(line)
        
        return merged_lines
    
    def _find_vertical_lines(self, binary_img: Image.Image) -> List[int]:
        """查找垂直线条"""
        width, height = binary_img.size
        v_lines = []
        
        for x in range(width):
            # 计算这一列的黑色像素比例
            col_pixels = list(binary_img.crop((x, 0, x + 1, height)).getdata())
            black_ratio = sum(1 for pixel in col_pixels if pixel == 0) / len(col_pixels)
            
            # 如果黑色像素比例高，认为是分镜线
            if black_ratio > 0.7:  # 70%以上是黑色
                v_lines.append(x)
        
        # 合并接近的线条
        merged_lines = []
        for line in v_lines:
            if not merged_lines or line - merged_lines[-1] > self.gap_threshold:
                merged_lines.append(line)
        
        return merged_lines
    
    def _build_panels_from_lines(self, h_lines: List[int], v_lines: List[int], 
                                img_size: Tuple[int, int]) -> List[Dict[str, Any]]:
        """基于线条构建分镜"""
        width, height = img_size
        panels = []
        
        # 添加边界
        h_boundaries = [0] + h_lines + [height]
        v_boundaries = [0] + v_lines + [width]
        
        # 构建分镜矩形
        for i in range(len(h_boundaries) - 1):
            for j in range(len(v_boundaries) - 1):
                y1, y2 = h_boundaries[i], h_boundaries[i + 1]
                x1, x2 = v_boundaries[j], v_boundaries[j + 1]
                
                # 过滤太小的分镜
                panel_width = x2 - x1
                panel_height = y2 - y1
                
                if panel_width >= self.min_panel_size and panel_height >= self.min_panel_size:
                    panels.append({
                        'bbox': [x1, y1, x2, y2],
                        'confidence': 0.8,
                        'panel_type': 'line_detected',
                        'detection_method': 'line_based'
                    })
        
        return panels
    
    async def _detect_panels_by_color_regions(self, image_path: str) -> List[Dict[str, Any]]:
        """基于颜色区域检测分镜"""
        try:
            with Image.open(image_path) as img:
                # 转换为灰度图
                gray = img.convert('L')
                
                # 使用连通区域分析
                panels = self._analyze_connected_regions(gray)
                
                return panels
                
        except Exception as e:
            self.logger.error(f"颜色区域检测分镜失败: {e}")
            return []
    
    def _analyze_connected_regions(self, gray_img: Image.Image) -> List[Dict[str, Any]]:
        """分析连通区域"""
        try:
            # 将图像转换为numpy数组
            img_array = np.array(gray_img)
            
            # 二值化
            threshold = self.black_threshold
            binary = (img_array > threshold).astype(np.uint8)
            
            # 查找连通区域
            from scipy import ndimage
            labeled, num_features = ndimage.label(binary)
            
            panels = []
            for i in range(1, num_features + 1):
                # 获取区域的边界框
                positions = np.where(labeled == i)
                if len(positions[0]) > 0:
                    y_min, y_max = positions[0].min(), positions[0].max()
                    x_min, x_max = positions[1].min(), positions[1].max()
                    
                    # 计算区域大小
                    width = x_max - x_min
                    height = y_max - y_min
                    
                    # 过滤太小的区域
                    if width >= self.min_panel_size and height >= self.min_panel_size:
                        panels.append({
                            'bbox': [x_min, y_min, x_max + 1, y_max + 1],
                            'confidence': 0.7,
                            'panel_type': 'color_region',
                            'detection_method': 'color_based'
                        })
            
            return panels
            
        except ImportError:
            # 如果没有scipy，使用简单的区域检测
            return self._simple_region_detection(gray_img)
        except Exception as e:
            self.logger.error(f"连通区域分析失败: {e}")
            return []
    
    def _simple_region_detection(self, gray_img: Image.Image) -> List[Dict[str, Any]]:
        """简单的区域检测（不依赖scipy）"""
        try:
            width, height = gray_img.size
            
            # 使用网格扫描检测内容区域
            grid_size = 50
            panels = []
            
            for y in range(0, height, grid_size):
                for x in range(0, width, grid_size):
                    # 检查这个网格区域是否有内容
                    region = gray_img.crop((
                        x, y, 
                        min(x + grid_size, width), 
                        min(y + grid_size, height)
                    ))
                    
                    # 计算平均亮度
                    stat = ImageStat.Stat(region)
                    avg_brightness = stat.mean[0]
                    
                    # 如果亮度足够高，认为有内容
                    if avg_brightness > self.black_threshold * 2:
                        # 扩展这个区域
                        expanded_bbox = self._expand_content_region(
                            gray_img, x, y, 
                            min(x + grid_size, width), 
                            min(y + grid_size, height)
                        )
                        
                        if expanded_bbox:
                            panels.append({
                                'bbox': expanded_bbox,
                                'confidence': 0.6,
                                'panel_type': 'simple_region',
                                'detection_method': 'simple_based'
                            })
            
            return panels
            
        except Exception as e:
            self.logger.error(f"简单区域检测失败: {e}")
            return []
    
    def _expand_content_region(self, gray_img: Image.Image, 
                             x1: int, y1: int, x2: int, y2: int) -> Optional[List[int]]:
        """扩展内容区域到实际边界"""
        try:
            width, height = gray_img.size
            
            # 向上扩展
            while y1 > 0:
                test_row = gray_img.crop((x1, y1 - 1, x2, y1))
                if ImageStat.Stat(test_row).mean[0] > self.black_threshold:
                    y1 -= 1
                else:
                    break
            
            # 向下扩展
            while y2 < height:
                test_row = gray_img.crop((x1, y2, x2, y2 + 1))
                if ImageStat.Stat(test_row).mean[0] > self.black_threshold:
                    y2 += 1
                else:
                    break
            
            # 向左扩展
            while x1 > 0:
                test_col = gray_img.crop((x1 - 1, y1, x1, y2))
                if ImageStat.Stat(test_col).mean[0] > self.black_threshold:
                    x1 -= 1
                else:
                    break
            
            # 向右扩展
            while x2 < width:
                test_col = gray_img.crop((x2, y1, x2 + 1, y2))
                if ImageStat.Stat(test_col).mean[0] > self.black_threshold:
                    x2 += 1
                else:
                    break
            
            # 检查最小尺寸
            if (x2 - x1) >= self.min_panel_size and (y2 - y1) >= self.min_panel_size:
                return [x1, y1, x2, y2]
            
            return None
            
        except Exception as e:
            self.logger.error(f"扩展内容区域失败: {e}")
            return None
    
    async def _detect_panels_by_ai(self, image_path: str) -> List[Dict[str, Any]]:
        """AI辅助分镜检测"""
        try:
            # 使用AI识别分镜边界 - 强化版提示词
            ai_prompt = """
请仔细分析这张漫画图片，精确识别其中的所有分镜（panel）边界。**特别重要：必须检测到图片的右侧边缘区域！**

**识别要求：**
1. **完整覆盖**：从图片最左侧到最右侧，每个分镜都要识别到
2. **右侧重点**：特别注意图片右侧的分镜，不能遗漏
3. **精确边界**：标记每个分镜的精确边界框坐标
4. **边缘检测**：检查图片的四条边缘，确保没有遗漏内容

**检测步骤：**
1. 从左到右扫描整个图片
2. 识别所有可见的分镜区域
3. 特别关注右侧1/3区域
4. 确认边界分镜是否完整

**返回格式（JSON）：**
{
  "panels": [
    {
      "bbox": [x1, y1, x2, y2],
      "description": "分镜内容描述",
      "confidence": 0.9,
      "position": "left/center/right"  // 标明位置
    }
  ],
  "total_panels": 4,
  "coverage_check": {
    "left_edge_detected": true,
    "right_edge_detected": true,
    "missing_areas": []
  },
  "analysis": "整体分析，特别说明右侧区域检测情况"
}

**重要提醒：**
- bbox格式：[左上角x, 左上角y, 右下角x, 右下角y]
- 坐标系统：左上角为(0,0)
- **必须检测到图片右侧的分镜！**
- 如果右侧有内容但不确定分镜边界，请标记出来
"""
            
            ai_result = await self._call_ollama_for_panel_detection(image_path, ai_prompt)
            
            if ai_result and 'panels' in ai_result:
                panels = []
                coverage_check = ai_result.get('coverage_check', {})
                
                # 检查右侧覆盖情况
                right_edge_detected = coverage_check.get('right_edge_detected', False)
                missing_areas = coverage_check.get('missing_areas', [])
                
                if not right_edge_detected:
                    self.logger.warning("AI检测：右侧边缘可能存在遗漏")
                
                if missing_areas:
                    self.logger.warning(f"AI检测：发现遗漏区域: {missing_areas}")
                
                for panel in ai_result['panels']:
                    bbox = panel.get('bbox', [0, 0, 0, 0])
                    if len(bbox) == 4:
                        panels.append({
                            'bbox': bbox,
                            'confidence': panel.get('confidence', 0.8),
                            'panel_type': 'ai_detected',
                            'detection_method': 'ai_based',
                            'description': panel.get('description', ''),
                            'position': panel.get('position', 'unknown')
                        })
                
                self.logger.info(f"AI检测到 {len(panels)} 个分镜")
                if ai_result.get('analysis'):
                    self.logger.info(f"AI分析: {ai_result['analysis']}")
                
                return panels
            
            return []
            
        except Exception as e:
            self.logger.error(f"AI分镜检测失败: {e}")
            return []
    
    async def _detect_right_edge_panels(self, image_path: str, width: int, height: int) -> List[Dict[str, Any]]:
        """右侧区域强化检测 - 专门检测右侧可能遗漏的分镜"""
        try:
            self.logger.info("开始右侧区域强化检测...")
            
            with Image.open(image_path) as img:
                # 转换为灰度图进行分析
                gray = img.convert('L')
                
                # 定义右侧检测区域（根据配置）
                right_region_width = int(width * self.right_edge_coverage)
                right_x_start = width - right_region_width
                
                right_edge_panels = []
                
                # 方法1：垂直扫描检测右侧分镜
                vertical_panels = self._vertical_scan_right_region(gray, right_x_start, width, height)
                right_edge_panels.extend(vertical_panels)
                
                # 方法2：水平切片检测右侧内容
                horizontal_panels = self._horizontal_slice_right_region(gray, right_x_start, width, height)
                right_edge_panels.extend(horizontal_panels)
                
                # 方法3：边界填充检测 - 确保覆盖到最右边缘
                boundary_panels = self._ensure_right_boundary_coverage(gray, right_x_start, width, height)
                right_edge_panels.extend(boundary_panels)
                
                # 去重和优化
                deduplicated_panels = self._deduplicate_panels(right_edge_panels)
                
                self.logger.info(f"右侧区域检测完成，发现 {len(deduplicated_panels)} 个潜在分镜")
                return deduplicated_panels
                
        except Exception as e:
            self.logger.error(f"右侧区域检测失败: {e}")
            return []
    
    def _vertical_scan_right_region(self, gray_img: Image.Image, x_start: int, x_end: int, height: int) -> List[Dict[str, Any]]:
        """垂直扫描右侧区域检测分镜"""
        panels = []
        
        # 将右侧区域分成垂直条带进行检测
        strip_width = 200  # 每个条带宽度
        num_strips = max(1, (x_end - x_start) // strip_width)
        
        for i in range(num_strips):
            strip_x1 = x_start + i * strip_width
            strip_x2 = min(strip_x1 + strip_width, x_end)
            
            # 检查这个条带是否有内容
            strip_region = gray_img.crop((strip_x1, 0, strip_x2, height))
            
            # 计算条带的平均亮度
            stat = ImageStat.Stat(strip_region)
            avg_brightness = stat.mean[0]
            
            # 如果亮度足够高，认为有内容（使用敏感度配置）
            if avg_brightness > self.black_threshold * self.right_edge_sensitivity:
                # 扩展这个条带到完整分镜
                expanded_panel = self._expand_strip_to_panel(gray_img, strip_x1, strip_x2, height)
                if expanded_panel:
                    panels.append(expanded_panel)
        
        return panels
    
    def _horizontal_slice_right_region(self, gray_img: Image.Image, x_start: int, x_end: int, height: int) -> List[Dict[str, Any]]:
        """水平切片检测右侧内容"""
        panels = []
        
        # 将右侧区域水平切片
        slice_height = 300  # 每个切片高度
        num_slices = max(1, height // slice_height)
        
        for i in range(num_slices):
            slice_y1 = i * slice_height
            slice_y2 = min(slice_y1 + slice_height, height)
            
            # 检查这个水平切片是否有内容
            slice_region = gray_img.crop((x_start, slice_y1, x_end, slice_y2))
            
            # 计算切片的平均亮度
            stat = ImageStat.Stat(slice_region)
            avg_brightness = stat.mean[0]
            
            # 如果亮度足够高，认为有内容（使用敏感度配置）
            if avg_brightness > self.black_threshold * self.right_edge_sensitivity:
                panels.append({
                    'bbox': [x_start, slice_y1, x_end, slice_y2],
                    'confidence': 0.6,
                    'panel_type': 'right_edge_horizontal',
                    'detection_method': 'right_edge_scan'
                })
        
        return panels
    
    def _ensure_right_boundary_coverage(self, gray_img: Image.Image, x_start: int, x_end: int, height: int) -> List[Dict[str, Any]]:
        """确保右边界覆盖 - 强制覆盖到最右边缘"""
        panels = []
        
        # 检查最右边缘是否有内容
        edge_width = 100  # 最右边缘宽度
        edge_region = gray_img.crop((x_end - edge_width, 0, x_end, height))
        
        stat = ImageStat.Stat(edge_region)
        avg_brightness = stat.mean[0]
        
        # 如果右边缘有内容，创建覆盖面板
        if avg_brightness > self.black_threshold * 1.5:
            # 创建覆盖整个右侧高度的面板
            panels.append({
                'bbox': [x_end - edge_width * 2, 0, x_end, height],
                'confidence': 0.7,
                'panel_type': 'right_edge_boundary',
                'detection_method': 'boundary_ensure'
            })
        
        return panels
    
    def _expand_strip_to_panel(self, gray_img: Image.Image, x1: int, x2: int, height: int) -> Optional[Dict[str, Any]]:
        """将检测到的条带扩展为完整分镜"""
        try:
            # 向上扩展
            y1 = 0
            while y1 < height:
                test_row = gray_img.crop((x1, y1, x2, y1 + 10))
                if ImageStat.Stat(test_row).mean[0] > self.black_threshold * 1.5:
                    y1 += 5
                else:
                    break
            
            # 向下扩展
            y2 = height
            while y2 > y1:
                test_row = gray_img.crop((x1, y2 - 10, x2, y2))
                if ImageStat.Stat(test_row).mean[0] > self.black_threshold * 1.5:
                    y2 -= 5
                else:
                    break
            
            # 检查最小尺寸
            if (x2 - x1) >= self.min_panel_size and (y2 - y1) >= self.min_panel_size:
                return {
                    'bbox': [x1, y1, x2, y2],
                    'confidence': 0.6,
                    'panel_type': 'right_edge_expanded',
                    'detection_method': 'right_edge_scan'
                }
            
            return None
            
        except Exception as e:
            self.logger.error(f"扩展条带失败: {e}")
            return None
    
    def _deduplicate_panels(self, panels: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """去重分镜，移除重叠度过高的分镜"""
        if not panels:
            return []
        
        deduplicated = []
        
        for panel in panels:
            bbox = panel['bbox']
            is_duplicate = False
            
            # 检查与已有分镜的重叠度
            for existing in deduplicated:
                existing_bbox = existing['bbox']
                
                # 计算重叠面积
                overlap_x1 = max(bbox[0], existing_bbox[0])
                overlap_y1 = max(bbox[1], existing_bbox[1])
                overlap_x2 = min(bbox[2], existing_bbox[2])
                overlap_y2 = min(bbox[3], existing_bbox[3])
                
                if overlap_x2 > overlap_x1 and overlap_y2 > overlap_y1:
                    overlap_area = (overlap_x2 - overlap_x1) * (overlap_y2 - overlap_y1)
                    panel_area = (bbox[2] - bbox[0]) * (bbox[3] - bbox[1])
                    
                    # 如果重叠度超过80%，认为是重复
                    if overlap_area / panel_area > 0.8:
                        is_duplicate = True
                        break
            
            if not is_duplicate:
                deduplicated.append(panel)
        
        return deduplicated
    
    async def _call_ollama_for_panel_detection(self, image_path: str, prompt: str) -> Optional[Dict[str, Any]]:
        """调用Ollama API进行分镜检测"""
        try:
            # 编码图片
            with open(image_path, 'rb') as f:
                image_data = f.read()
            base64_image = base64.b64encode(image_data).decode('utf-8')
            
            # 准备请求数据
            payload = {
                "model": self.get_current_model(),
                "prompt": prompt,
                "images": [base64_image],
                "stream": False,
                "options": {
                    "temperature": 0.1,
                    "top_p": 0.9,
                    "max_tokens": 2048
                }
            }
            
            # 发送请求
            response = requests.post(
                f"{self.base_url}/api/generate",
                json=payload,
                timeout=self.ai_timeout  # 使用配置的超时时间
            )
            
            if response.status_code == 200:
                result = response.json()
                content = result.get('response', '')
                
                # 尝试解析JSON响应
                try:
                    start_idx = content.find('{')
                    end_idx = content.rfind('}') + 1
                    
                    if start_idx != -1 and end_idx > start_idx:
                        json_str = content[start_idx:end_idx]
                        analysis_result = json.loads(json_str)
                        return analysis_result
                    else:
                        self.logger.warning("无法从AI响应中提取JSON")
                        return None
                        
                except json.JSONDecodeError as e:
                    self.logger.error(f"AI响应JSON解析失败: {e}")
                    return None
            else:
                self.logger.error(f"AI API请求失败: {response.status_code}")
                return None
                
        except Exception as e:
            self.logger.error(f"调用AI API失败: {e}")
            return None
    
    async def _merge_and_optimize_panels(self, line_panels: List[Dict[str, Any]], 
                                       color_panels: List[Dict[str, Any]], 
                                       ai_panels: List[Dict[str, Any]],
                                       right_edge_panels: List[Dict[str, Any]],
                                       width: int, height: int) -> List[Dict[str, Any]]:
        """合并和优化分镜检测结果"""
        try:
            all_panels = line_panels + color_panels + ai_panels + right_edge_panels
            
            if not all_panels:
                return []
            
            self.logger.info(f"合并前分镜统计: 线条{len(line_panels)}, 颜色{len(color_panels)}, AI{len(ai_panels)}, 右侧{len(right_edge_panels)}")
            
            # 去重：移除重叠度高的分镜
            deduplicated_panels = self._remove_overlapping_panels(all_panels)
            
            # 合并小分镜（如果启用）
            if self.merge_small_panels:
                merged_panels = self._merge_small_panels(deduplicated_panels)
            else:
                merged_panels = deduplicated_panels
            
            # 排序：按位置排序（从左到右，从上到下）
            sorted_panels = self._sort_panels_by_position(merged_panels)
            
            # 最终验证
            final_panels = []
            for panel in sorted_panels:
                bbox = panel['bbox']
                if self._validate_panel(bbox, width, height):
                    final_panels.append(panel)
            
            self.logger.info(f"优化后得到 {len(final_panels)} 个分镜")
            return final_panels
            
        except Exception as e:
            self.logger.error(f"分镜合并优化失败: {e}")
            return all_panels[:1] if all_panels else []
    
    def _remove_overlapping_panels(self, panels: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """移除重叠的分镜"""
        try:
            if not panels:
                return []
            
            # 按置信度排序
            sorted_panels = sorted(panels, key=lambda x: x['confidence'], reverse=True)
            
            filtered_panels = []
            for panel in sorted_panels:
                bbox = panel['bbox']
                is_overlapping = False
                
                for existing in filtered_panels:
                    existing_bbox = existing['bbox']
                    if self._calculate_overlap_ratio(bbox, existing_bbox) > 0.7:
                        is_overlapping = True
                        break
                
                if not is_overlapping:
                    filtered_panels.append(panel)
            
            return filtered_panels
            
        except Exception as e:
            self.logger.error(f"移除重叠分镜失败: {e}")
            return panels
    
    def _calculate_overlap_ratio(self, bbox1: List[int], bbox2: List[int]) -> float:
        """计算两个边界框的重叠比例"""
        try:
            x1_1, y1_1, x2_1, y2_1 = bbox1
            x1_2, y1_2, x2_2, y2_2 = bbox2
            
            # 计算交集
            x1_i = max(x1_1, x1_2)
            y1_i = max(y1_1, y1_2)
            x2_i = min(x2_1, x2_2)
            y2_i = min(y2_1, y2_2)
            
            if x2_i <= x1_i or y2_i <= y1_i:
                return 0.0
            
            intersection_area = (x2_i - x1_i) * (y2_i - y1_i)
            area1 = (x2_1 - x1_1) * (y2_1 - y1_1)
            area2 = (x2_2 - x1_2) * (y2_2 - y1_2)
            
            return intersection_area / min(area1, area2)
            
        except Exception as e:
            self.logger.error(f"计算重叠比例失败: {e}")
            return 0.0
    
    def _merge_small_panels(self, panels: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """合并小分镜"""
        try:
            # 简单实现：过滤太小的分镜
            filtered_panels = []
            for panel in panels:
                bbox = panel['bbox']
                width = bbox[2] - bbox[0]
                height = bbox[3] - bbox[1]
                
                if width >= self.min_panel_size and height >= self.min_panel_size:
                    filtered_panels.append(panel)
            
            return filtered_panels
            
        except Exception as e:
            self.logger.error(f"合并小分镜失败: {e}")
            return panels
    
    def _sort_panels_by_position(self, panels: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """按位置排序分镜（从左到右，从上到下）"""
        try:
            return sorted(panels, key=lambda x: (x['bbox'][1], x['bbox'][0]))
        except Exception as e:
            self.logger.error(f"分镜排序失败: {e}")
            return panels
    
    def _validate_panel(self, bbox: List[int], width: int, height: int) -> bool:
        """验证分镜边界框"""
        try:
            x1, y1, x2, y2 = bbox
            
            # 检查边界
            if x1 < 0 or y1 < 0 or x2 > width or y2 > height:
                return False
            
            if x2 <= x1 or y2 <= y1:
                return False
            
            # 检查最小尺寸
            panel_width = x2 - x1
            panel_height = y2 - y1
            
            if panel_width < self.min_panel_size or panel_height < self.min_panel_size:
                return False
            
            return True
            
        except Exception as e:
            self.logger.error(f"分镜验证失败: {e}")
            return False
    
    async def _save_panel_visualization(self, file_info: Dict[str, Any], panels: List[Dict[str, Any]]):
        """保存分镜可视化结果"""
        try:
            if not self.config.get('output', {}).get('save_bbox_visualization', True):
                return
            
            with Image.open(file_info['processed_path']) as img:
                draw = ImageDraw.Draw(img)
                
                # 为每个分镜绘制边界框
                for i, panel in enumerate(panels):
                    bbox = panel['bbox']
                    confidence = panel.get('confidence', 0.5)
                    
                    # 根据置信度选择颜色
                    if confidence > 0.8:
                        color = 'green'
                    elif confidence > 0.6:
                        color = 'blue'
                    else:
                        color = 'red'
                    
                    # 绘制边界框
                    draw.rectangle(bbox, outline=color, width=3)
                    
                    # 添加分镜编号
                    x1, y1 = bbox[0], bbox[1]
                    draw.text((x1 + 5, y1 + 5), f"#{i+1}", fill='white')
                
                # 保存可视化结果
                vis_filename = f"panels_{file_info.get('original_name', file_info.get('processed_name', 'unknown'))}"
                output_dir = self.config.get('output', {}).get('output_dir', 'output')
                vis_path = Path(output_dir) / 'visualizations' / vis_filename
                
                vis_path.parent.mkdir(parents=True, exist_ok=True)
                img.save(vis_path)
                
                self.logger.info(f"分镜可视化已保存: {vis_path}")
                
        except Exception as e:
            self.logger.error(f"保存分镜可视化失败: {e}")